/*
 * z80_opcodes.cpp
 *
 *  Created on: Jan 14, 2012
 *      Author: douglas
 */

#include <iostream>
#include "z80.hpp"

using namespace std;

inline void z80::op_adc_8bits() {
	u16 tmp_A = regs.A + regs.s + regs.Flags.c;
	regs.A = tmp_A;
	//setFlags(tmp_A);
}

inline void z80::op_adc_16bits() {
	u32 tmp_A = regs.HL + regs.ss + regs.Flags.c;
	regs.HL = tmp_A;
	//setFlags(tmp_A);
}




inline void z80::op_add_8bits() {
	u16 tmp_A = regs.A + regs.s;
	regs.A = tmp_A;

	//FLAGS
	regs.Flags.h = !!( (regs.s ^ regs.A ^ tmp_A) & 0x10 ); //TODO: Check this. This isn't my code
	regs.Flags.pv = (regs.A ^ regs.s ^ 0x80) & (regs.A ^ tmp_A) & 0x80; //TODO: Check this. This isn't my code
	regs.Flags.z = tmp_A == 0;
	regs.Flags.c = tmp_A & 0x100;
	regs.Flags.s = tmp_A & 0x80;
	regs.Flags.n = 0;
}

template <u8 pairIdxReg>
inline void z80::op_add_16bits() {
	u32 tmp_A = *regs.pair[opcode.sourceRegPair] + regs.ss;
	*regs.pair[pairIdxReg] = tmp_A;
	//setFlags(tmp_A);
}




inline void z80::op_and_8bits() {
	u16 tmp_A = regs.A & regs.s;
	regs.A = tmp_A;
	//setFlags(tmp_A);
}





inline void z80::op_bit() {
	regs.Flags.z = (regs.s & (1<<opcode.bitNumber)) ? 1 : 0;
	//FLAGS
}

template<bool hascondition> inline void z80::op_call() {
	if (hascondition)
	{
		//TODO:
	}
	//push(regs.pc); //TODO: implement this
	//FLAGS
}



inline void z80::op_ccf() {
	regs.Flags.c = !regs.Flags.c;
	//FLAGS
}


inline void z80::op_cp() {
	u16 tmp_A = regs.A - regs.s;
	//setFlags(tmp_A);
}

inline void z80::op_cpd() {
	u16 tmp_A = regs.A - regs.s;
	regs.HL--;
	regs.BC--;
	//setFlags(tmp_A);
}


inline void z80::op_cpdr() {
	u16 tmp_A = regs.A - regs.s;
	regs.HL--;
	regs.BC--;

	//setFlags(tmp_A);

	if ((regs.BC != 0) && (regs.A != regs.s))
	{
		regs.pc -= 2; //re-execute the intruction
	}
}


inline void z80::op_cpi() {
	u16 tmp_A = regs.A - regs.s;
	regs.HL++;
	regs.BC--;
	//setFlags(tmp_A);
}


inline void z80::op_cpir() {
	u16 tmp_A = regs.A - regs.s;
	regs.HL++;
	regs.BC--;

	//setFlags(tmp_A);

	if ((regs.BC != 0) && (regs.A != regs.s))
	{
		regs.pc -= 2; //re-execute the intruction
	}
}


inline void z80::op_cpl() {
	regs.A = ~regs.A;
	//setFlags(tmp_A);
}


inline void z80::op_daa() {
	u8 correctionFactor = 0;

	if ((regs.A > 0x99) || (regs.Flags.c))
	{
		correctionFactor = 6 << 4;
		regs.Flags.c = 1;
	}
	else
	{
		regs.Flags.c = 0;
	}

	if ( ((regs.A & 0x0F) > 9) || (regs.Flags.h))
	{
		correctionFactor |= 6;
	}

	if (!regs.Flags.n)
	{
		regs.A += correctionFactor;
	}
	else
	{
		regs.A -= correctionFactor;
	}

	//setFlags(tmp_A);
}



inline void z80::op_dec_8bits() {
	*regs.r[opcode.regToDec]--;
	//setFlags();
}

template<u8 regPair>
inline void z80::op_dec_16bits() {
	*regs.pair[regPair]--;
	//setFlags();
}

inline void z80::op_di() {
	irq.IFF1 = false;
	irq.IFF2 = false;
}

inline void z80::op_djnz() {
	regs.B--;

	if (regs.B)
	{
		regs.pc += (signed char) regs.s; //TODO: change signed char to a defined type.

	}

}

inline void z80::op_ei() {
	irq.IFF1 = true;
	irq.IFF2 = true;
}








template<bool hascondition> inline void z80::op_jp()
{
	if (hascondition)
	{
		if (!z80_condition(opcode.condition)) return; //if condition not met
	}

	regs.pc = regs.ss;
}

template<bool hascondition> inline void z80::op_jr()
{
	if (hascondition)
	{
		if (!z80_condition(opcode.condition_rel)) return; //if condition not met, return.
	}

	regs.pc += (s8) regs.s;

	//sync(5);If condition was met or the jumo has not a condition, we still have to sync 5T.
}







inline void z80::op_illegal() {
	cout << "Invalid Instruction: " << hex << opcode << endl;
}









template <bool fromMemory> inline void z80::op_load_to_reg_auto() {
	if (fromMemory) *regs.r[opcode.destReg] = read8(regs.ss, 3);
	else *regs.r[opcode.destReg] = regs.s;
}
template <u8 IdxReg, bool fromMemory> inline void z80::op_load_to_reg_manual() {
	if (fromMemory) *regs.r[IdxReg] = read8(regs.ss, 3);
	else *regs.r[IdxReg] = regs.s;
}



template <bool fromMemory> inline void z80::op_load_to_pair_auto() {
	if (fromMemory)
	{
		*regs.pair[opcode.sourceRegPair] = read8(regs.ss, 3);
		*regs.pair[opcode.sourceRegPair] |= read8(regs.ss + 1, 3) << 8;
	}
	else *regs.pair[opcode.sourceRegPair] = regs.ss;
}
template <u8 pairIdxReg, bool fromMemory> inline void z80::op_load_to_pair_manual() {
	if (fromMemory)
	{
		*regs.pair[pairIdxReg] = read8(regs.ss, 3);
		*regs.pair[pairIdxReg] |= read8(regs.ss + 1, 3) << 8;
	}
	else *regs.pair[pairIdxReg] = regs.ss;
}


inline void z80::op_load_reg_to_mem_auto() {
	write8(regs.ss, *regs.r[opcode.sourceReg], 3);
}
template <u8 IdxReg> inline void z80::op_load_reg_to_mem_manual() {
	write8(regs.ss, *regs.r[IdxReg], 3);
}

inline void z80::op_load_pair_to_mem_auto() {
	write8(regs.ss, (*regs.pair[opcode.sourceRegPair] & 0x00FF), 3);
	write8(regs.ss+1, *regs.pair[opcode.sourceRegPair] >> 8, 3);
}
template <u8 pairIdxReg> inline void z80::op_load_pair_to_mem_manual() {
	write8(regs.ss, (*regs.pair[pairIdxReg] & 0x00FF), 3);
	write8(regs.ss+1, *regs.pair[pairIdxReg] >> 8, 3);
}


inline void z80::op_ldd() {
	regs.s = read8(regs.ss, 3);
	write8(regs.DE, regs.s, 3);
	regs.HL--;
	regs.DE--;
	regs.BC--;
	regs.Flags.h = 0;
	regs.Flags.n = 0;
	regs.Flags.pv = (regs.BC) ? 1 : 0;
	sync(2);
}

inline void z80::op_lddr() {
	z80::op_ldd();

	if (regs.BC)
	{
		regs.pc -= 2;
		sync(5);
	}
}

inline void z80::op_ldi() {
	regs.s = read8(regs.ss, 3);
	write8(regs.DE, regs.s, 3);
	regs.HL++;
	regs.DE++;
	regs.BC--;
	regs.Flags.h = 0;
	regs.Flags.n = 0;
	regs.Flags.pv = (regs.BC) ? 1 : 0;
	sync(2);
}

inline void z80::op_ldir() {
	z80::op_ldi();

	if (regs.BC)
	{
		regs.pc -= 2;
		sync(5);
	}
}

inline void z80::op_load_imm_to_mem() {
	write8(regs.ss, regs.s, 3);
}


inline void z80::op_neg() {
	u16 result = 0 - regs.A;

	regs.A = result;

	//flags - same from sub
}

inline void z80::op_nop() {
#ifdef Z80_DEBUG
	cout << "NOP" << endl;
#endif

}


inline void z80::op_or_8bits() {
	u16 tmp_A = regs.A | regs.s;
	regs.A = tmp_A;
	//setFlags(tmp_A);
}


inline void z80::op_otdr() {
	write8(regs.C, regs.s, 4); //TODO: Verify the bus. Book say Register B and C are used to match the address.

	regs.B--;
	regs.HL--;

	if (regs.BC)
	{
		regs.pc -= 2;
		sync(5);
	}
}

inline void z80::op_otir() {
	write8(regs.C, regs.s, 4); //TODO: Verify the bus. Must be IO. Book say Register B and C are used to match the address.

	regs.B--;
	regs.HL++;

	if (regs.BC)
	{
		regs.pc -= 2;
		sync(5);
	}
}


inline void z80::op_out() {
	write8(regs.C, regs.s, 3); //TODO: Verify the bus. Must be IO. Book say Register B and C are used to match the address.
}

inline void z80::op_out_imm() { //opcode D3
	write8(regs.s, regs.A, 3); //TODO: Verify the bus. Must be IO. Book say Register B and C are used to match the address.
}

inline void z80::op_outd() {
	write8(regs.C, regs.s, 4); //TODO: Verify the bus. Must be IO. Book say Register B and C are used to match the address.

	regs.B--;
	regs.HL--;
}

inline void z80::op_outi() {
	write8(regs.C, regs.s, 4); //TODO: Verify the bus. Must be IO. Book say Register B and C are used to match the address.

	regs.B--;
	regs.HL++;
}


inline void z80::op_pop_auto() {
	*regs.pair[opcode.destRegPair] = read8(regs.ss++, 3);
	regs.sp++;
	*regs.pair[opcode.destRegPair] |= read8(regs.ss++ + 1, 3) << 8;
	regs.sp++;
}

template <u8 pairIdxReg> inline void z80::op_pop_manual() {
	*regs.pair[pairIdxReg] = read8(regs.ss, 3);
	regs.sp++;
	*regs.pair[pairIdxReg] |= read8(regs.ss+1, 3) << 8;
	regs.sp++;
}


inline void z80::op_push_auto() {
	write8(--regs.ss, *regs.pair[opcode.destRegPair] >> 8, 3);
	regs.sp--;
	write8(--regs.ss, *regs.pair[opcode.destRegPair] && 0xFF, 3);
	regs.sp--;
}

template <u8 pairIdxReg> inline void z80::op_push_manual() {
	write8(--regs.ss, *regs.pair[pairIdxReg] >> 8, 3);
	regs.sp--;
	write8(--regs.ss, *regs.pair[pairIdxReg] && 0xFF, 3);
	regs.sp--;
}





inline void z80::op_res_reg_auto() {
	*regs.r[opcode.sourceReg] &= ~(1 << opcode.bitNumber);
}

inline void z80::op_res_mem() {
	regs.s &= ~(1 << opcode.bitNumber);
	write8(regs.ss, regs.s, 3);
}


template<bool hascondition>  inline void z80::op_ret() {

#ifdef Z80_TEST
	retCallback(0);
#endif

	if (hascondition)
	{
		if (!z80_condition(opcode.condition)) return; //if condition not met
	}


	regs.pc = read8(regs.ss++, 3);
	regs.sp++;
	regs.pc |= read8(regs.ss++, 3) << 8;
	regs.sp++;
}

inline void z80::op_reti() {
	regs.pc = read8(regs.ss++, 3);
	regs.sp++;
	regs.pc |= read8(regs.ss++, 3) << 8;
	regs.sp++;

	irq.IFF1 = irq.IFF2;
}

inline void z80::op_retn() {
	regs.pc = read8(regs.ss++, 3);
	regs.sp++;
	regs.pc |= read8(regs.ss++, 3) << 8;
	regs.sp++;

	irq.IFF1 = irq.IFF2;
}


inline void z80::op_rl() {
	bool carry = regs.Flags.c;
	regs.Flags.c = regs.s & 0x80;
	regs.s <<= 1;
	regs.s |= carry;

	*regs.r[opcode.sourceReg] = regs.s;
}

template <u8 pairIdxReg> inline void z80::op_rl_mem() {
	bool carry = regs.Flags.c;
	regs.Flags.c = regs.s & 0x80;
	regs.s <<= 1;
	regs.s |= carry;

	write8(*regs.r[pairIdxReg], regs.s, 3);
}


inline void z80::op_rlc() {
	regs.Flags.c = regs.s & 0x80;
	regs.s <<= 1;
	regs.s |= regs.Flags.c;

	*regs.r[opcode.sourceReg] = regs.s;
}

template <u8 pairIdxReg> inline void z80::op_rlc_mem() {
	regs.Flags.c = regs.s & 0x80;
	regs.s <<= 1;
	regs.s |= regs.Flags.c;

	write8(*regs.r[pairIdxReg], regs.s, 3);
}


inline void z80::op_rld() {
	u8 a_temp = regs.A;
	regs.A = (regs.A & 0xF0) | (regs.s >> 4);
	regs.s = (regs.s << 4) | (a_temp & 0xF);

	write8(regs.HL, regs.s, 4);
	sync(3);
}



inline void z80::op_rr() {
	bool carry = regs.Flags.c;
	regs.Flags.c = regs.s & 0x1;
	regs.s >>= 1;
	regs.s |= carry<<7;

	*regs.r[opcode.sourceReg] = regs.s;
}

template <u8 pairIdxReg> inline void z80::op_rr_mem() {
	bool carry = regs.Flags.c;
	regs.Flags.c = regs.s & 0x1;
	regs.s >>= 1;
	regs.s |= carry<<7;

	write8(*regs.r[pairIdxReg], regs.s, 3);
}


inline void z80::op_rrc() {
	regs.Flags.c = regs.s & 0x01;
	regs.s >>= 1;
	regs.s |= regs.Flags.c << 7;

	*regs.r[opcode.sourceReg] = regs.s;
}




